/*
 * @(#) $Header$
 *
 * Copyright (C) 2010  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.crypto.md;


/**
 * This class embodies the algorithm for the message digest SHA-256 according to
 * <a target="external" href="http://csrc.nist.gov/cryptval/shs.html">The Secure
 * Hash Standard</a> document release by NIST. It is more elegant to get an
 * instance with the factory method in class
 * {@link java.security.MessageDigest} :
 * <blockquote>
 * <code>MessageDigest sha256 = MessageDigest.getInstance(Forklabs.SHA_256, Forklabs.NAME);</code>
 * </blockquote>
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.crypto.md.SHA256">Daniel Léonard</a>
 * @version $Revision$
 * @see      ca.forklabs.crypto.Forklabs
 */
public class SHA256 extends SmallSHA {

//---------------------------
// Class variables
//---------------------------

   /** The name of this message digest. */
   @SuppressWarnings("nls")
   public static final String NAME = "SHA256";

   private static final int[] K = new int[] {
      0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
      0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
      0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
      0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
      0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
      0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
      0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
      0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
      0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
      0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
      0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
      0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
      0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
      0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
      0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
      0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
      };

   /** The length in bytes of the digest. */
   public static final int DIGEST_LEN = 32;

   /** The length in bytes of a block. */
   public static final int BUFFER_LEN = 64;

   /** The size of the H buffer. */
   public static final int H_LEN = 8;

   /** The size of the W buffer. */
   public static final int W_LEN = 64;


//----------------------------
// Constructor
//----------------------------

   /**
    * Constructor.
    */
   public SHA256() {
      super(SHA256.NAME, SHA256.BUFFER_LEN, SHA256.DIGEST_LEN, SHA256.H_LEN, SHA256.W_LEN);
      this.coreInit();
      }


//----------------------------
// Scrambling methods
//----------------------------

   private static int Ch(int x, int y, int z) {
      return ((x & y) ^ (~x & z));
      }

   private static int Maj(int x, int y, int z) {
      return ((x & y) ^ (x & z) ^ (y & z));
      }

   private static int SIGMA0(int x) {
      return (AbstractBlockMessageDigest.rotateRight(x, 2) ^ AbstractBlockMessageDigest.rotateRight(x, 13) ^ AbstractBlockMessageDigest.rotateRight(x, 22));
      }

   private static int SIGMA1(int x) {
      return (AbstractBlockMessageDigest.rotateRight(x, 6) ^ AbstractBlockMessageDigest.rotateRight(x, 11) ^ AbstractBlockMessageDigest.rotateRight(x, 25));
      }

   private static int sigma0(int x) {
      return (AbstractBlockMessageDigest.rotateRight(x, 7) ^ AbstractBlockMessageDigest.rotateRight(x, 18) ^ (x >>> 3));
      }

   private static int sigma1(int x) {
      return (AbstractBlockMessageDigest.rotateRight(x, 17) ^ AbstractBlockMessageDigest.rotateRight(x, 19) ^ (x >>> 10));
      }


//----------------------------
// Concrete core methods
//----------------------------

   @Override
   protected void coreTransform() {
      byte[] buffer = this.getBuffer();
      int[] w = this.getWBuffer();
      w[ 0] =  AbstractBlockMessageDigest.makeInt(buffer,  0,  1,  2,  3);
      w[ 1] =  AbstractBlockMessageDigest.makeInt(buffer,  4,  5,  6,  7);
      w[ 2] =  AbstractBlockMessageDigest.makeInt(buffer,  8,  9, 10, 11);
      w[ 3] =  AbstractBlockMessageDigest.makeInt(buffer, 12, 13, 14, 15);
      w[ 4] =  AbstractBlockMessageDigest.makeInt(buffer, 16, 17, 18, 19);
      w[ 5] =  AbstractBlockMessageDigest.makeInt(buffer, 20, 21, 22, 23);
      w[ 6] =  AbstractBlockMessageDigest.makeInt(buffer, 24, 25, 26, 27);
      w[ 7] =  AbstractBlockMessageDigest.makeInt(buffer, 28, 29, 30, 31);
      w[ 8] =  AbstractBlockMessageDigest.makeInt(buffer, 32, 33, 34, 35);
      w[ 9] =  AbstractBlockMessageDigest.makeInt(buffer, 36, 37, 38, 39);
      w[10] =  AbstractBlockMessageDigest.makeInt(buffer, 40, 41, 42, 43);
      w[11] =  AbstractBlockMessageDigest.makeInt(buffer, 44, 45, 46, 47);
      w[12] =  AbstractBlockMessageDigest.makeInt(buffer, 48, 49, 50, 51);
      w[13] =  AbstractBlockMessageDigest.makeInt(buffer, 52, 53, 54, 55);
      w[14] =  AbstractBlockMessageDigest.makeInt(buffer, 56, 57, 58, 59);
      w[15] =  AbstractBlockMessageDigest.makeInt(buffer, 60, 61, 62, 63);
      for (int i = 16; i < 64; i++) {
         w[i] = SHA256.sigma1(w[i - 2]) + w[i - 7] + SHA256.sigma0(w[i - 15]) + w[i - 16];
         }

      AbstractBlockMessageDigest.zeroize(buffer);

      int[] H = this.getHBuffer();
      int a = H[0];
      int b = H[1];
      int c = H[2];
      int d = H[3];
      int e = H[4];
      int f = H[5];
      int g = H[6];
      int h = H[7];

      int T1 = 0; int T2 = 0;
      for (int i = 0; i < 64; i++) {
         T1 = h + SHA256.SIGMA1(e) + SHA256.Ch(e, f, g) + SHA256.K[i] + w[i];
         T2 = SHA256.SIGMA0(a) + SHA256.Maj(a, b, c);
         h = g;
         g = f;
         f = e;
         e = d + T1;
         d = c;
         c = b;
         b = a;
         a = T1 + T2;
         }

      AbstractBlockMessageDigest.zeroize(w);

      H[0] += a;
      H[1] += b;
      H[2] += c;
      H[3] += d;
      H[4] += e;
      H[5] += f;
      H[6] += g;
      H[7] += h;
      }

   @Override
   protected void coreInit() {
      int[] h = this.getHBuffer();
      h[0] = 0x6a09e667;
      h[1] = 0xbb67ae85;
      h[2] = 0x3c6ef372;
      h[3] = 0xa54ff53a;
      h[4] = 0x510e527f;
      h[5] = 0x9b05688c;
      h[6] = 0x1f83d9ab;
      h[7] = 0x5be0cd19;

      int[] w = this.getWBuffer();
      AbstractBlockMessageDigest.zeroize(w);
      }

   }

